package org.origin.centre.security.configs;

import org.origin.centre.security.point.IAuthenticationEntryPoint;
import org.origin.centre.security.properties.AuthorizationProperty;
import org.origin.centre.security.token.JwtAuthenticationConverter;
import org.origin.centre.security.utils.RequestMatcherUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.web.SecurityFilterChain;

/**
 * 默认安全配置
 *
 * @author ferret
 * @version 2024-05-08
 */
@SuppressWarnings({"SpringJavaInjectionPointsAutowiringInspection", "SpringFacetCodeInspection"})
@EnableWebSecurity
@EnableMethodSecurity
@Configuration(proxyBeanMethods = false)
public class AutoSecurityConfig {

    /**
     * Spring Security 安全过滤器链配置
     *
     * @param http 安全配置
     * @return 安全过滤器链
     */
    @Bean
    @Order(0)
    public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, Converter<Jwt, ? extends AbstractAuthenticationToken> jwtAuthenticationConverter) throws Exception {
        http.csrf(AbstractHttpConfigurer::disable);
        http.formLogin(Customizer.withDefaults());

        http.authorizeHttpRequests((requests) -> {
            requests.anyRequest().authenticated();
        });

        // 配置OAuth2资源服务器
        http.oauth2ResourceServer(oauth2 -> {
            oauth2.jwt(jwtConfigurer -> {
                jwtConfigurer.jwtAuthenticationConverter(jwtAuthenticationConverter);
            });
            oauth2.authenticationEntryPoint(new IAuthenticationEntryPoint());
        });

        http.exceptionHandling((exceptions) -> {
            exceptions.authenticationEntryPoint(new IAuthenticationEntryPoint());
        });

        return http.build();
    }

    /**
     * Spring Security 自定义安全配置
     */
    @Bean
    public WebSecurityCustomizer webSecurityCustomizer(AuthorizationProperty property) {
        return (web) ->
                // 不走过滤器链(场景：静态资源js、css、html)
                web.ignoring()
                        .requestMatchers(HttpMethod.OPTIONS)
                        .requestMatchers(RequestMatcherUtil.getAntPathRequestMatchers(property.getPermitAllUrls()));
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    @Bean
    public Converter<Jwt, ? extends AbstractAuthenticationToken> jwtAuthenticationConverter(RedisTemplate<Object, Object> redisTemplate, AuthorizationProperty property) {
        return new JwtAuthenticationConverter(redisTemplate, property.getTokenCacheKKey());
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}
